﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using Aquarinum.Base.Infrastructure.EventBus.EventStore;
using Aquarinum.Base.Infrastructure.EventBus.Interfaces;
using Aquarinum.Base.Infrastructure.EventBus.RabbitMQWrapper;
using Aquarinum.Base.Infrastructure.Ioc;
using EasyNetQ;
using IEventBus = Aquarinum.Base.Infrastructure.EventBus.Interfaces.IEventBus;

namespace Aquarinum.Base.Infrastructure.EventBus.Default
{
    /// <summary>
    ///     RabbitMQ事件总线默认实现
    /// </summary>
    public class DefaultRabbitMqBus : IEventBus
    {
        /// <summary>
        ///     定义锁对象
        /// </summary>
        private static readonly object LockObj = new object();

        private readonly IEventStore _eventStore;

        private readonly RabbitMqClient _mqClient;

        /// <summary>
        ///     mq 订阅结果缓存，key为event类型
        /// </summary>
        private readonly ConcurrentDictionary<Type, ISubscriptionResult> _rabbirMqSubscriptionMapping =
            new ConcurrentDictionary<Type, ISubscriptionResult>();


        /// <inheritdoc />
        public DefaultRabbitMqBus(IEventStore eventStore, RabbitMqClient mqClient)
        {
            _mqClient = mqClient;
            _eventStore = eventStore;
        }

        /// <inheritdoc />
        public DefaultRabbitMqBus()
        {
            _mqClient = new RabbitMqClient(@"localhost");
            //TODO IOC 获取IEventStore实现？
            _eventStore = DefalutInMemoryEventStore.GetInstance();
        }

        #region Register

        /// <inheritdoc />
        public void Register<TEventData, THandler>() where TEventData : class, IEventData where THandler : IEventHandler
        {
            //注册IEventHandler<T>到IOC容器
            AquainumContainer.RegisterGeneric(typeof(THandler), typeof(IEventHandler<TEventData>));
            //todo  生成订阅id
            _eventStore.AddRegister<TEventData, THandler>();
            lock (LockObj)
            {
                if (!HasRegisterForEvent<TEventData>())
                {
                    var subscriptionResult = _mqClient.Subscribe<TEventData>(Guid.NewGuid().ToString(),
                        msg =>
                        {
                            var runner = AquainumContainer.Resolve<IHandlerRunner<TEventData>>();
                            runner.ExcuteHandler(msg);
                        });
                    _rabbirMqSubscriptionMapping.TryAdd(typeof(TEventData), subscriptionResult);
                }
            }
        }

        #endregion

        #region publish

        public void Publish<TEventData>(TEventData eventData) where TEventData : class, IEventData
        {
            _mqClient.Publish(eventData);
        }

        #endregion

        #region rabbirMqSubscriptionMapping

        /// <summary>
        ///     判断是否已注册（泛型）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public bool HasRegisterForEvent<T>() where T : class, IEventData
        {
            return _rabbirMqSubscriptionMapping.ContainsKey(typeof(T));
        }

        /// <summary>
        ///     判断是否已注册
        /// </summary>
        /// <returns></returns>
        public bool HasRegisterForEvent(Type handlerType)
        {
            return _rabbirMqSubscriptionMapping.ContainsKey(handlerType);
        }

        /// <summary>
        ///     获取泛型event所有的handler
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public ISubscriptionResult GetMqSubscriptionForEvent<T>() where T : class, IEventData
        {
            if (HasRegisterForEvent<T>()) return _rabbirMqSubscriptionMapping[typeof(T)];

            return null;
        }

        #endregion

        #region UnRegister

        private readonly object disposelock = new object();

        void IEventBus.UnRegister<TEventData>(Type handlerType)
        {
            _eventStore.RemoveRegister(typeof(TEventData), handlerType);
            //如果所有handler全部取消，则关闭rabbitmq订阅
            var handlertypes = _eventStore.GetHandlersForEvent<TEventData>();
            if (!handlertypes.Any())
            {
                var rabbirMqSubscription = GetMqSubscriptionForEvent<TEventData>();
                lock (disposelock)
                {
                    rabbirMqSubscription?.Dispose();
                }
            }
        }

        void IEventBus.UnRegisterAll<TEventData>()
        {
            var handlerTypes = _eventStore.GetHandlersForEvent(typeof(TEventData)).ToList();
            foreach (var handlerType in handlerTypes) _eventStore.RemoveRegister(typeof(TEventData), handlerType);
        }

        #endregion
    }
}